package util

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"golang.org/x/crypto/bcrypt"
	"io/ioutil"
	"log"
	"os"
)

// 生成密文
func GenerateFromPassword(password string) string {
	pass := []byte(password)
	hp, err := bcrypt.GenerateFromPassword(pass, 0)
	if err != nil {
		log.Printf("GenerateFromPassword error: %s\n", err)
	}

	return string(hp)
}

// 对比密码第一个传密文，第二个传md5值
func CompareHashAndPassword(hp string, pass string) bool {
	if bcrypt.CompareHashAndPassword([]byte(hp), []byte(pass)) != nil {
		log.Printf("%v should hash %s correctly\n", hp, pass)
		return false
	}
	return true
}

// Sign
//
//	@Author achilles
//	@Description: 私钥加签
//	@param plainText
//	@param privateKeyPath
//	@date 2022-04-25 14:56:18
//	@return string
//	@return error
func Sign(plainText string, privateKeyPath string) (string, error) {
	certFile, err := ioutil.ReadFile(privateKeyPath)
	if err != nil {
		return "", err
	}
	block, _ := pem.Decode(certFile)
	if block == nil {
		return "", errors.New("util bcrypt Sign PrivateKey error")
	}
	private, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return "", err
	}
	h := crypto.Hash.New(crypto.SHA1) //进行SHA1的散列
	h.Write([]byte(plainText))
	hashed := h.Sum(nil)
	// 进行rsa加密签名
	signedData, err := rsa.SignPKCS1v15(rand.Reader, private, crypto.SHA1, hashed)
	if err != nil {
		return "", err
	}
	data := base64.StdEncoding.EncodeToString(signedData)

	return data, nil
}

func PubSign(src []byte, pubKeyPath string) (string, error) {
	certFile, err := ioutil.ReadFile(pubKeyPath)
	if err != nil {
		return "", err
	}
	block, _ := pem.Decode(certFile)
	// 使用X509将解码之后的数据 解析出来
	publicKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
	if err != nil {
		return "", err
	}
	// 使用公钥加密数据
	pkcs1v15, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, src)
	if err != nil {
		return "", err
	}
	return string(pkcs1v15), nil
}

// Verify
//
//	@Author achilles
//	@Description: 公钥验签
//	@param originalData
//	@param signData
//	@date 2022-04-27 19:20:14
//	@return error
func Verify(originalData, signData string, publicKeyPath string) error {
	certFile, err := ioutil.ReadFile(publicKeyPath)
	if err != nil {
		return err
	}
	block, _ := pem.Decode(certFile)
	if block == nil {
		return errors.New("failed to parse PEM block containing the public key")
	}
	sign, err := base64.StdEncoding.DecodeString(signData)
	if err != nil {
		return err
	}
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return err
	}
	hash := sha1.New()
	hash.Write([]byte(originalData))
	return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA1, hash.Sum(nil), sign)
}

func loadRSAPrivateKey(filename string) (*rsa.PrivateKey, error) {
	privateKeyBytes, err := os.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(privateKeyBytes)
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	return privateKey, nil
}

func DecryptPKCS1v15(filename string, ciphertext string) (string, error) {
	privateKey, err := loadRSAPrivateKey(filename)
	if err != nil {
		return "", err
	}
	ciphertextBytes, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return "", err
	}
	// Decrypt using the private key
	decrypted, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertextBytes)
	if err != nil {
		return "", err
	}
	return string(decrypted), nil
}
